#include "stdinc.h"
#include "Video.h"
#include "InputMgr.h"
#include "cmd/cvar.h"

#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>

#include "resource.h"

#define WINDOW_CLASS	"Fili's OGL app"

class CVideoPlatform
{
public:
	HDC			hDC;
	HGLRC		hRC;
	HWND		hWnd;
	HINSTANCE	hInstance;
};

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

CVideo::CVideo()
{
	m_platform = new CVideoPlatform;

	memset(m_platform, 0, sizeof(CVideoPlatform));
}

CVideo::~CVideo()
{
}

bool CVideo::Restart()
{
	KillGLWindow();
	return CreateGLWindow();
}

bool CVideo::Suhutdown()
{
	KillGLWindow();
	return false;
}

void CVideo::Flip()
{
	SwapBuffers(m_platform->hDC);
}

bool CVideo::ProcessMsg()
{
	MSG		msg;

	if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		if (msg.message == WM_QUIT)
		{
			cvar_set_bool(CVAR_QUIT, true);
			return false;
		}
		else
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return true;
}

bool CVideo::CreateGLWindow()
{
	GLuint		PixelFormat;
	WNDCLASS	wc;
	DWORD		dwExStyle;
	DWORD		dwStyle;
	RECT		WindowRect;

	m_fullscreen	= cvar_get_bool(CVAR_VID_FULLSCREEN);
	m_width			= cvar_get_int(CVAR_VID_WIDTH);
	m_height		= cvar_get_int(CVAR_VID_HEIGHT);
	m_bpp			= cvar_get_int(CVAR_VID_BPP);

	WindowRect.left		= 0;
	WindowRect.right	= m_width;
	WindowRect.top		= 0;
	WindowRect.bottom	= m_height;


	m_platform->hInstance	= GetModuleHandle(NULL);
	wc.style				= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc			= (WNDPROC) WndProc;
	wc.cbClsExtra			= 0;
	wc.cbWndExtra			= 0;
	wc.hInstance			= m_platform->hInstance;
	wc.hIcon				= LoadIcon(m_platform->hInstance, MAKEINTRESOURCE(IDI_ICON1));
	wc.hCursor				= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground		= NULL;
	wc.lpszMenuName			= NULL;
	wc.lpszClassName		= WINDOW_CLASS;

	if (!RegisterClass(&wc))
	{
		LOG->Error("Failed To Register The Window Class.");
		return false;
	}

	if (m_fullscreen)
	{
		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth	= cvar_get_int(CVAR_VID_WIDTH);
		dmScreenSettings.dmPelsHeight	= cvar_get_int(CVAR_VID_HEIGHT);
		dmScreenSettings.dmBitsPerPel	= cvar_get_int(CVAR_VID_BPP);
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				m_fullscreen = false;
			}
			else
			{
				LOG->Error("Program Will Now Close.");
				return false;
			}
		}
	}

	if (m_fullscreen)
	{
		dwExStyle = WS_EX_APPWINDOW;
		dwStyle = WS_POPUP;
		ShowCursor(false);
	}
	else
	{
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle = WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

	m_platform->hWnd=CreateWindowEx(
		dwExStyle,
		WINDOW_CLASS,
		cvar_get_string(CVAR_WND_TITLE),
		dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
		cvar_get_int(CVAR_VID_XPOS),
		cvar_get_int(CVAR_VID_YPOS),
		WindowRect.right-WindowRect.left,
		WindowRect.bottom-WindowRect.top,
		NULL,
		NULL,
		m_platform->hInstance,
		NULL);

	if (!m_platform->hWnd)
	{
		KillGLWindow();
		LOG->Error("Window Creation Error.");
		return false;
	}

	static	PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		m_bpp,
		0, 0, 0, 0, 0, 0,
		0,
		0,
		0,
		0, 0, 0, 0,
		16,											// 16Bit Z-Buffer (Depth Buffer)
		0,											// No Stencil Buffer
		0,
		PFD_MAIN_PLANE,
		0, 0, 0, 0
	};

	m_platform->hDC=GetDC(m_platform->hWnd);
	if (!m_platform->hDC)
	{
		KillGLWindow();
		LOG->Error("Can't Create A GL Device Context.");
		return false;
	}

	PixelFormat=ChoosePixelFormat(m_platform->hDC,&pfd);
	if (!PixelFormat)
	{
		KillGLWindow();
		LOG->Error("Can't Find A Suitable PixelFormat.");
		return false;
	}

	if(!SetPixelFormat(m_platform->hDC,PixelFormat,&pfd))
	{
		KillGLWindow();
		LOG->Error("Can't Set The PixelFormat.");
		return false;
	}

	m_platform->hRC=wglCreateContext(m_platform->hDC);
	if (!m_platform->hRC)
	{
		KillGLWindow();
		LOG->Error("Can't Create A GL Rendering Context.");
		return false;
	}

	if(!wglMakeCurrent(m_platform->hDC,m_platform->hRC))
	{
		KillGLWindow();
		LOG->Error("Can't Activate The GL Rendering Context.");
		return false;
	}

	ShowWindow(m_platform->hWnd, SW_SHOW);
	SetForegroundWindow(m_platform->hWnd);
	SetFocus(m_platform->hWnd);

	glViewport(0, 0, m_width, m_height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(
		cvar_get_float(CVAR_FOV),
		(GLfloat)cvar_get_int(CVAR_VID_WIDTH)/(GLfloat)cvar_get_int(CVAR_VID_HEIGHT),
		cvar_get_float(CVAR_ZBUF_MIN),
		cvar_get_float(CVAR_ZBUF_MAX));

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix

	return TRUE;									// Success

}

void CVideo::KillGLWindow()
{
	if (m_fullscreen)
	{
		ChangeDisplaySettings(NULL,0);
		ShowCursor(TRUE);
	}

	if (m_platform->hRC)
	{
		if (!wglMakeCurrent(NULL,NULL))
		{
			LOG->Error("Release Of DC And RC Failed.");
		}

		if (!wglDeleteContext(m_platform->hRC))
		{
			LOG->Error("Release Rendering Context Failed.");
		}
		m_platform->hRC=NULL;
	}

	if (m_platform->hDC && !ReleaseDC(m_platform->hWnd,m_platform->hDC))
	{
		LOG->Error("Release Device Context Failed.");
		m_platform->hDC=NULL;
	}

	if (m_platform->hWnd && !DestroyWindow(m_platform->hWnd))
	{
		LOG->Error("Could Not Release hWnd.");
		m_platform->hWnd=NULL;
	}

	if (!UnregisterClass(WINDOW_CLASS, m_platform->hInstance))
	{
		LOG->Error("Could Not Unregister Class.");
		m_platform->hInstance=NULL;
	}

}

typedef struct
{
	int code;
	int key;
}TKeyCode;

static TKeyCode keyCodes[] = 
{
	{VK_ESCAPE,			KEY_ESCAPE},
	{VK_OEM_3,			KEY_TILDE},
	{VK_TAB,			KEY_TAB},
	{VK_CAPITAL,		KEY_CAPS},
	{VK_SHIFT,			KEY_SHIFT},
	{VK_CONTROL,		KEY_CTRL},
	{VK_MENU,			KEY_ALT},

	{VK_OEM_MINUS,		KEY_MINUS},
	{VK_OEM_PLUS,		KEY_EQUAL},
	{VK_OEM_4,			KEY_LBRKT},
	{VK_OEM_6,			KEY_RBRKT},
	{VK_OEM_5,			KEY_BKSLH},
	{VK_OEM_1,			KEY_COLON},
	{VK_OEM_7,			KEY_QUOTE},
	{VK_OEM_2,			KEY_SLASH},
	{VK_OEM_COMMA,		KEY_COMMA},
	{VK_OEM_PERIOD,		KEY_PERIOD},
	{VK_SNAPSHOT,		KEY_PRTSCR},
	{VK_SCROLL,			KEY_SCRLCK},
	{VK_PAUSE,			KEY_PAUSE},
	{VK_RETURN,			KEY_ENTER},
	{VK_SPACE,			KEY_SPACE},
	{VK_BACK,			KEY_BKSP},
	{VK_INSERT,			KEY_INS},
	{VK_DELETE,			KEY_DEL},
	{VK_HOME,			KEY_HOME},
	{VK_END,			KEY_END},
	{VK_PRIOR,			KEY_PGUP},
	{VK_NEXT,			KEY_PGDN},
	{VK_UP,				KEY_UPARROW},
	{VK_DOWN,			KEY_DOWNARROW},
	{VK_LEFT,			KET_LEFTARROW},
	{VK_RIGHT,			KEY_RIGHTARROW},
	{VK_NUMLOCK,		KEY_KPNUMLOCK},
	{0,				KEY_KPDIV},
	{0,				KEY_KPMUL},
	{0,				KEY_KPMINUS},
	{0,				KEY_KPPLUS},
	{0,				KEY_KPENTER},
	{0,				KEY_KPDEL},
};

int TranslateKeyCode(int code)
{
	if ((code>='A')&&(code<='Z'))
		return KEY_A + (code - 'A');

	if ((code>='0')&&(code<='9'))
		return KEY_0 + (code - '0');

	if ((code>=VK_F1)&&(code<=VK_F12))
		return KEY_F1 + (code - VK_F1);

	if ((code>=VK_NUMPAD0)&&(code<=VK_NUMPAD9))
		return KEY_KP0 + (code - VK_NUMPAD0);

	int n = sizeof(keyCodes) / sizeof(keyCodes[0]);

	for (int i=0; i<n; i++)
	{
		if (keyCodes[i].code == code)
			return keyCodes[i].key;
	}

	return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_ACTIVATE:
		{
			if (!HIWORD(wParam))
			{
//				m_platform->active = true;
			}
			else
			{
//				m_platform->active = false;
			}

			return 0;
		}

	case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
			case SC_SCREENSAVE:
			case SC_MONITORPOWER:
			case SC_KEYMENU:
				return 0;
			}
			break;
		}

	case WM_CLOSE:
		{
			PostQuitMessage(0);
			return 0;
		}

	case WM_SYSKEYDOWN:
	case WM_KEYDOWN:
		{
			INPUTMGR->KeyDown(TranslateKeyCode(wParam));
			return 0;
		}

	case WM_SYSKEYUP:
	case WM_KEYUP:
		{
			INPUTMGR->KeyUp(TranslateKeyCode(wParam));
			return 0;
		}

	case WM_CHAR:
		{
			INPUTMGR->Char(wParam);
			return 0;
		}

	case WM_MOVE:
		{
//			if (!m_platform->fullscreen)
//			{
//				cvar_set_int(CVAR_VID_XPOS, LOWORD(lParam));
//				cvar_set_int(CVAR_VID_YPOS, HIWORD(lParam));
//			}
		}

//	case WM_SIZE:
//		{
//			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
//			video_updateWindow();
//			return 0;
//		}
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}